#ifndef VECTOR_H
#define VECTOR_H

#include <cmath>

namespace MathGraphics
{
	struct Vector
	{
		double x;
		double y;
		double z;

		explicit Vector(double xc, double yc, double zc);
		Vector(Vector* vec);
		
		Vector normalize() const;
		double length() const;

		Vector operator +(const Vector& v) const;
		Vector operator -(const Vector& v) const;
		Vector operator /(double factor) const;
		Vector& operator +=(const Vector& v);
		Vector& operator -=(const Vector& v);
		Vector& operator /=(double factor);
		double operator *(const Vector& v) const;
	};


	Vector crossProduct(const Vector& v1, const Vector& v2);
	Vector operator *(double factor, const Vector& v);
	Vector operator *(const Vector& v, double factor);
	bool operator ==(const Vector& v1, const Vector& v2);
	bool operator !=(const Vector& v1, const Vector& v2); 

	inline Vector::Vector(double xc = 0.0f, double yc = 0.0f, double zc = 0.0f) : x(xc), y(yc), z(zc) {}

	inline Vector::Vector(Vector* vec) : x(vec->x), y(vec->y), z(vec->z) {}

	inline Vector Vector::normalize() const
	{
		double l = 1.0f / length();

		return (Vector(x * l, y * l, z * l));
	}

	inline double Vector::length() const
	{
		return std::sqrt((x * x) + (y * y) + (z * z));
	}

	inline Vector Vector::operator +(const Vector& v) const
	{
		return Vector(this->x + v.x, this->y + v.y, this->z + v.z);
	}

	inline Vector Vector::operator -(const Vector& v) const
	{
		return Vector(this->x - v.x, this->y - v.y, this->z - v.z);
	}

	inline Vector Vector::operator /(double factor) const
	{
		double invf = 1.0f / factor;

		return Vector(this->x * invf, this->y * invf, this->z * invf);
	}

	inline Vector& Vector::operator +=(const Vector& v) 
	{
		this->x += v.x;
		this->y += v.y;
		this->z += v.z;

		return *this;
	}

	inline Vector& Vector::operator -=(const Vector& v)
	{
		this->x -= v.x;
		this->y -= v.y;
		this->z -= v.z;

		return *this;
	}

	inline Vector& Vector::operator /=(double factor)
	{
		double invf = 1.0f / factor;

		this->x *= invf;
		this->y *= invf;
		this->z *= invf;

		return *this;
	}

	inline double Vector::operator *(const Vector& v) const
	{
		double r;

		r = (this->x * v.x) + (this->y * v.y) + (this->z * v.z);

		return r;
	}

	inline Vector operator *(double factor, const Vector& v)
	{
		return Vector(factor * v.x, factor * v.y, factor * v.z);
	}

	inline Vector operator *(const Vector& v, double factor)
	{
		return Vector(factor * v.x, factor * v.y, factor * v.z);
	}

	inline Vector crossProduct(const Vector& v1, const Vector& v2)
	{
		return Vector((v1.y * v2.z) - (v1.z * v2.y), (v1.z * v2.x) - (v1.x * v2.z), (v1.x * v2.y) - (v1.y * v2.x));
	}

	inline bool operator ==(const Vector& v1, const Vector& v2)
	{
		return (v1.x == v2.x && v1.y == v2.y && v1.z == v2.z);
	}

	inline bool operator !=(const Vector& v1, const Vector& v2)
	{
		return (v1.x != v2.x && v1.y != v2.y && v1.z != v2.z);
	}
}


#endif